﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Compiler
{
    class CSharpTokenListGenerator 
    {
        List<Token> Generate()
        {
            List<Token> Tokens = new List<Token>();

            //RegEx reservierte Zeichen
            //\ * + ? | { [ () ^ $ . #     
            pushKeywords(Tokens);
            pushOperators(Tokens);
            pushComplexTokens(Tokens);

            return Tokens;
        }

        private void pushComplexTokens(List<Token> Tokens)
        {
            Tokens.Add(new Token(@"\#.*", "PREPROC", Int32.MaxValue));
            Tokens.Add(new Token(@"//.*", "COMMENT", Int32.MaxValue));
            Tokens.Add(new Token(@"/\*(.|\s)*?\*/", "COMMENT", Int32.MaxValue));
            Tokens.Add(new Token(@"""(\\.|[^\\""\n])*""", "STRLTRL", Int32.MaxValue));
            Tokens.Add(new Token(@"@""([^""]|"""")*""", "VERBSTRLTRL", Int32.MaxValue));
            Tokens.Add(new Token(@"@?[a-zA-Z_][a-zA-Z_0-9]*", "IDENTIFIER", Int32.MaxValue));
            Tokens.Add(new Token(@"[0-9]*[xX]?\.?[0-9]+([eE][-+]?[0-9]+)?[fFmM]?", "NUM", Int32.MaxValue)); //[-+]?
        }

        private void pushOperators(List<Token> Tokens)
        {
            Tokens.Add(new Token(@"\{", "LEFTCURLYBRACKET", 1));
            Tokens.Add(new Token(@"\}", "RIGHTCURLYBRACKET", 1));
            Tokens.Add(new Token(@"\(", "LEFTBRACKET", 1));
            Tokens.Add(new Token(@"\)", "RIGHTBRACKET", 1));
            Tokens.Add(new Token(@"\[", "LEFTSQUAREBRACKET", 1));
            Tokens.Add(new Token(@"\]", "RIGHTSQUAREBRACKET", 1));
            Tokens.Add(new Token("<", "LEFTANGLEBRACKET", 1));
            Tokens.Add(new Token(">", "RIGHTANGLEBRACKET", 1));
            Tokens.Add(new Token("=", "ASSIGN", 1));
            Tokens.Add(new Token(";", "SEMICOLON", 1));
            Tokens.Add(new Token(",", "COMMA", 1));
            Tokens.Add(new Token(":", "COLON", 1));
            Tokens.Add(new Token("::", "SCOPECOLON", 2));
            Tokens.Add(new Token(@"\.", "SCOPE", 1));
            Tokens.Add(new Token(@"\?", "QSTMRK", 1));
            Tokens.Add(new Token(@"\?\?", "DBLQSTMRK", 2));
            Tokens.Add(new Token("=>", "LMDA", 2));
            Tokens.Add(new Token("==|!=", "EQUAL", 2));
            Tokens.Add(new Token("<=|>=", "RELEQUAL", 2));
            Tokens.Add(new Token(@"\+|\*|/|-|%", "ARITH", 1));
            Tokens.Add(new Token(@"\+=|\*=|/=|-=|%=", "ARITHASSIGN", 2));
            Tokens.Add(new Token(@"\+\+|--", "UNRYARITH", 2));
            Tokens.Add(new Token("!", "UNRYLOGIC", 1));
            Tokens.Add(new Token(@"&&|\|\|", "BINRYLOGIC", 2));
            Tokens.Add(new Token(@"~", "BITUNRYOP", 1));
            Tokens.Add(new Token(@"\||&|\^", "BITBINRYOP", 1));
            Tokens.Add(new Token(@"\|=|&=|\^=", "BITASSIGN", 2));
            Tokens.Add(new Token("<<|>>", "BITSHFT", 2));
            Tokens.Add(new Token("<<=|>>=", "BITSHFTASSIGN", 3));
            Tokens.Add(new Token(@"'\\?.'", "CHARLTRL", 4));
        }

        void pushKeywords(List<Token> Tokens)
        {
            string[] keywords = new[]{
                    "abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked", "class",
                    "const", "continue", "decimal", "default", "delegate", "do", "double", "else", "enum", "event",
                    "explicit", "extern", "false", "finally", "fixed", "float", "for", "foreach", "goto", "if", "implicit",
                    "in", "int", "interface", "internal", "is", "lock", "long", "namespace", "new", "null", "object",
                    "operator", "out", "override", "params", "private", "protected", "public", "readonly", "ref", "return",
                    "sbyte", "sealed", "short", "sizeof", "stackalloc", "static", "string", "struct", "switch", "this",
                    "throw", "true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", "using", "virtual",
                    "void", "volatile", "while", "add", "ascending", "by", "descending", "dynamic", "from", "get", "global",
                    "group", "into", "join", "let", "orderby", "partial", "remove", "select", "set", "value", "var", "where", "yield"
            };

            ////slow
            //foreach (var keyword in keywords)
            //    keywordTokens.Add(new Token(keyword, keyword.ToUpper(), keyword.Length));

            ////fast
            string join = @"\G\b(" + string.Join("|", keywords) + @")\b";
            Tokens.Add(new Token(join, "KEYWORD", keywords.Max(s => s.Length)));
        }
    }
}
